<?php
/** Yz_SQL_Statement_Select
*
*/

class Yz_SQL_Statement_Select
    extends Yz_SQL_DMLStatement
    implements Yz_SQL_Statement_Interface
{

    const CLAUSE_SELECT = 'SELECT';
    const CLAUSE_CALC_FOUND_ROWS = 'SQL_CALC_FOUND_ROWS';
    const CLAUSE_DISTINCT = 'DISTINCT';
    const CLAUSE_FIELDS = '_fields_';
    const CLAUSE_FROM = 'FROM';
    const CLAUSE_TABLES = '_tables_';
    const CLAUSE_WHERE = 'WHERE';
    const CLAUSE_GROUP_BY = 'GROUP BY';
    const CLAUSE_HAVING = 'HAVING';
    const CLAUSE_ORDER_BY = 'ORDER BY';
    const CLAUSE_LIMIT = 'LIMIT';
    const CLAUSE_FOR_UPDATE = 'FOR UPDATE';

    protected $clauses = array(
        self::CLAUSE_SELECT     => '',
        self::CLAUSE_DISTINCT   => '',
        self::CLAUSE_CALC_FOUND_ROWS => '',
        self::CLAUSE_FIELDS     => '',
        self::CLAUSE_FROM       => '',
        self::CLAUSE_TABLES     => '',
        self::CLAUSE_WHERE      => '',
        self::CLAUSE_GROUP_BY   => '',
        self::CLAUSE_HAVING     => '',
        self::CLAUSE_ORDER_BY   => '',
        self::CLAUSE_LIMIT      => '',
        self::CLAUSE_FOR_UPDATE => '',
    );

    /***********************************
     ********** Fetching ***************
     **********************************/


    /** fetch() :
    *   @return <Yz_Hash>
    */
    public function fetch()
    {
        return $this->db->fetch();
    } // fetch()

    /** fetchAll() :
    *   @return <Yz_Array>
    */
    public function fetchAll()
    {
        return $this->db->fetchAll();
    } // fetchAll()

    /** fetchScalar() :
    *   @return <scalar>
    */
    public function fetchScalar()
    {
        return $this->db->fetchScalar();
    } // fetchScalar()

    /** fetchIndexed() :
    *   @return Yz_Array_Interface
    */
    public function fetchIndexed()
    {
        throw new Yz_Exception_NotImplemented();
    } // fetchIndexed()



    /***************************************
     ********** Constructor ****************/

    /**
     *  @param mixed $fields: either
     *      - a string, or
     *      - an array type, in which case the array's elements will be joined
     *  @return this
     *  @throw Yz_Exception_InvalidArgument
     */
    public function select( $fields )
    {
        if (is_string($fields)) {
            $f = Yz_String::testNotEmpty($fields);
        } else {
            $fieldsArray = Yz_Array::valueOf($fields);
            // at least one non-empty argument is required
            if (null === $fieldsArray || ! $fieldsArray) {
                // TODO subst correct exception
                throw new Yz_Exception_InvalidArgument();
            }
            $f = join( ",\n  ", $fieldsArray );
        }
#        $fieldsArray = $this->_fields($fields);
        $clause = "  " . $f;
        $this->sql = null;
        $this->clauses[self::CLAUSE_SELECT] = self::CLAUSE_SELECT;
        $this->clauses[self::CLAUSE_FIELDS] = $clause;

        return $this;
    }

    public function calcFoundRows( $do=true )
    {
        $c = self::CLAUSE_CALC_FOUND_ROWS;
        $this->clauses[$c] = $do ? $c : "";
        return $this;
    }

    public function getFoundRows()
    {
        $db = $this->testQueryDone()->db;
        $c = self::CLAUSE_CALC_FOUND_ROWS;
        if ($this->clauses[$c] === "" ) {
            $c = $db->count();
        } else {
            $c = $db
                ->execute("SELECT FOUND_ROWS()")
                ->fetchScalar();
        }
        return $c;
    }

    public function from( $table, $alias=null )
    {
        return $this->_table(self::CLAUSE_FROM, $table, $alias);
    }

    /**
     *  Default is a comma join
     *  an entry is a Hash with the following keys possible:
     *      - name: string, the name of the table - MANDATORY
     *      - alias: string
     *      - join: {LEFT|RIGHT|INNER|...} - if not supplied, the comma join is used
     *      - joinOn: the conditions for the ON clause
     *      - joinUsing: the conditions for the USING clause (unless joinOn is set)
     *  @param Yz_Hash $tables
     */
    public function join( Yz_Hash $tables )
    {
        debug();
        return $this->_join($tables);
    }

    /**
     *  @param array-type[ (alias:) Yz_SQL_Join ] $tableList
     */
    public function joinTables( $tableList )
    {
        debug();
        return $this->_joinTables($tableList);
    }

    /**
     *
     *  @param Yz_SQL_Join $args
     */
    public function joinTable( Yz_SQL_Join $table )
    {
        debug();
        return $this->_joinTable($table);
    }

    public function where( $what )
    {
        return $this->_where(self::CLAUSE_WHERE, $what, self::LOGICAL_AND);
    }

    public function orWhere( $what )
    {
        return $this->_where(self::CLAUSE_WHERE, $what, self::LOGICAL_OR);
    }

    public function groupBy( $what )
    {
        return $this->_groupBy(self::CLAUSE_GROUP_BY, $what);
    }

    public function having( $what )
    {
        return $this->_where(self::CLAUSE_HAVING, $what, self::LOGICAL_AND);
    }

    public function orderBy( $what )
    {
        return $this->_orderBy(self::CLAUSE_ORDER_BY, $what);
    }


    /**
     *  @param int offset: if param:limit is null, then param:offset is treated as limit, as in the SQL LIMIT clause
     *  @param int|null limit
     *
     */
    public function limit( $offset, $limit=null )
    {
        if (null === $offset) {
            $offset = 0;
        } else {
            $offset = Yz_Int::testNotNegative( $offset );
        }
        if (null === $limit) {
            $limit = $offset;
            $offset = 0;
        } else {
            $limit = Yz_Int::testNotNegative( $limit );
        }
        return $this->_limit($offset, $limit);
    }



    /**
     *  @return string SQL
     */
    protected function isComplete()
    {
        debug();

        $clauses = $this->clauses;

        return ! ( "" === $clauses[self::CLAUSE_SELECT] || "" === $clauses[self::CLAUSE_FROM] );
    } // testComplete


} // class

